﻿using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ZeroSum.Domain.Repositories;
using ZeroSum.Domain.UnitOfWork;
using ZeroSum.Exceptions;
using ZeroSum.Extend.EFCore.MySql.Constants;
using ZeroSum.Extend.EFCore.MySql.EntityFrameworks;

namespace ZeroSum.Extend.EFCore.MySql;

public static class EFCoreExtensions
{
    /// <summary>
    ///     添加EFCore, 并添加默认仓储
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <param name="optionsAction"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    public static IServiceCollection AddEFCore(this IServiceCollection services, IConfiguration configuration,
        Action<DbContextOptionsBuilder>? optionsAction = default, Action<IServiceCollection>? action = default)
    {
        action?.Invoke(services);

        // 注册上下文
        services.AddContextPool<ZeroSumDbContext>(configuration, optionsAction);

        // 注册仓储与工作单元
        services.AddTransient(typeof(IUnitOfWork), typeof(EFCoreUnitOfWork<ZeroSumDbContext>));
        services.AddTransient(typeof(IEfCoreRepository), typeof(EfCoreRepository<ZeroSumDbContext>));
        services.AddTransient(typeof(IRepository), typeof(EfCoreRepository<ZeroSumDbContext>));
        services.AddTransient(typeof(IRepository<>), typeof(DefaultContextCoreRepository<>));
        services.AddTransient(typeof(IRepository<,>), typeof(DefaultContextCoreRepository<,>));
        return services;
    }

    /// <summary>
    ///     添加EFCore，并注册Context，与相关的仓库、工作单元
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <param name="action"></param>
    /// <typeparam name="TContext"></typeparam>
    /// <returns></returns>
    public static IServiceCollection AddEFCore<TContext>(this IServiceCollection services, IConfiguration configuration,
        Action<IServiceCollection>? action = default)
        where TContext : DbContext
    {
        action?.Invoke(services);

        // 注册上下文
        services.AddContextPool<TContext>(configuration);

        // 注册仓库与工作单元
        services.AddTransient(typeof(IUnitOfWork), typeof(EFCoreUnitOfWork<TContext>));

        // TODO： 仓储以目前的情况，无法注入

        return services;
    }

    /// <summary>
    ///     添加DbContext Pool，并注入相关内容
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <param name="optionsAction"></param>
    /// <typeparam name="TContext"></typeparam>
    /// <returns></returns>
    public static IServiceCollection AddContextPool<TContext>(this IServiceCollection services,
        IConfiguration configuration, Action<DbContextOptionsBuilder>? optionsAction = default)
        where TContext : DbContext
    {
        var connections = configuration.GetConnectionString(DbConstants.DefaultConnectionStringName);
        if (connections == null) throw SystemConfigurationError.Of("系统配置没有正确配置连接字符串！");

        var serverVersion = new MySqlServerVersion(ServerVersion.AutoDetect(connections));

        services.AddDbContextPool<TContext>((serviceProvider, optionsBuilder) =>
        {
            optionsAction?.Invoke(optionsBuilder);
            optionsBuilder.UseMySql(connections,
                serverVersion, sqlOptions =>
                {
                    sqlOptions.MigrationsAssembly(Assembly.GetExecutingAssembly().GetName().Name);
                    sqlOptions.EnableRetryOnFailure(10, TimeSpan.FromSeconds(30), null);
                });

            var accessor = serviceProvider.GetService<IHttpContextAccessor>() ?? null;
            optionsBuilder.AddInterceptors(new ZeroSumSaveChangesInterceptor(accessor!));
        });

        return services;
    }
}